package main

import (
	"bufio"
	"context"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"os"
	"path/filepath"
	"regexp"
	"strings"

	"go-file-perception-model/internal/config"
)

// Tool descriptor for tools/list
func ToolRegexSearchDescriptor() Tool {
	return Tool{
		Name:        "regex_search",
		Description: "使用正则表达式搜索文件内容，返回结果按匹配数目从多到少排序，每个文件显示匹配行数和内容预览（最多显示3个匹配）",
		InputSchema: map[string]any{
			"type": "object",
			"properties": map[string]any{
				"pattern":        map[string]any{"type": "string", "description": "必填，Go正则表达式"},
				"directories":    map[string]any{"type": "array", "items": map[string]any{"type": "string"}, "description": "可选：限制搜索目录；相对路径将相对索引目录补全"},
				"case_insensitive": map[string]any{"type": "boolean", "default": false},
				"limit_files":    map[string]any{"type": "integer", "default": 100, "description": "最多返回多少个文件"},
				"max_matches_per_file": map[string]any{"type": "integer", "default": 5},
				"include_snippet": map[string]any{"type": "boolean", "default": true},
				"context_lines":  map[string]any{"type": "integer", "default": 1, "description": "代码片段上下文行数"},
			},
			"required": []string{"pattern"},
		},
	}
}

// Non-stream call handler (aggregated)
func CallRegexSearch(args map[string]any) (any, error) {
	pattern, _ := args["pattern"].(string)
	if pattern == "" {
		return nil, fmt.Errorf("pattern required")
	}
	ci, _ := args["case_insensitive"].(bool)
	limitFiles := 100
	if v, ok := args["limit_files"].(float64); ok { limitFiles = int(v) }
	maxPerFile := 5
	if v, ok := args["max_matches_per_file"].(float64); ok { maxPerFile = int(v) }
	includeSnippet := true
	if v, ok := args["include_snippet"].(bool); ok { includeSnippet = v }
	ctxLines := 1
	if v, ok := args["context_lines"].(float64); ok { ctxLines = int(v) }

	userDirs := extractStringArray(args["directories"]) 
	resolvedDirs, err := resolveDirectories(userDirs)
	if err != nil { return nil, err }

	re, err := compileRegex(pattern, ci)
	if err != nil { return nil, err }

	results := make([]map[string]any, 0, limitFiles)
	filesScanned := 0
	for _, root := range resolvedDirs {
		if len(results) >= limitFiles { break }
		filepath.WalkDir(root, func(path string, d os.DirEntry, err error) error {
			if err != nil { return nil }
			if d.IsDir() { return nil }
			if len(results) >= limitFiles { return io.EOF }
			// quick binary check and size guard
			if isLikelyBinary(path) { return nil }
			matches, err := searchFileForRegex(path, re, maxPerFile, includeSnippet, ctxLines)
			filesScanned++
			if err == nil && len(matches) > 0 {
				// aggregate per file (dedup)
				var indexDir string
				if cfg, e := config.LoadConfig(); e == nil && len(cfg.FileIndex.IndexDirectories) > 0 { indexDir = cfg.FileIndex.IndexDirectories[0] }
				relPath := path
				if indexDir != "" { relPath = getRelativePath(path, indexDir) }
				res := map[string]any{
					"path":        relPath,
					"matches":     matches,
					"match_count": len(matches),
				}
				results = append(results, res)
			}
			return nil
		})
	}
	
	// 按匹配数目从多到少排序
	sortResultsByMatchCount(results)
	
	// Convert results to MCP standard format
	processedResults := make([]map[string]any, 0, len(results))
	for _, result := range results {
		// Create text content from file matches
		textContent := fmt.Sprintf("File: %s\nMatch count: %d\n", result["path"], result["match_count"])
		
		// Add match details (最多显示3个匹配)
		if matches, ok := result["matches"].([]map[string]any); ok {
			fmt.Printf("DEBUG: Found %d matches for file %s\n", len(matches), result["path"])
			fmt.Printf("DEBUG: Type of matches: %T\n", matches)
			displayCount := len(matches)
			if displayCount > 3 {
				displayCount = 3
			}
			
			for i := 0; i < displayCount; i++ {
				match := matches[i]
				fmt.Printf("DEBUG: Processing match %d: %+v\n", i, match)
				fmt.Printf("DEBUG: Type of match: %T\n", match)
				// 支持int和float64两种类型的行号
				var lineNum int
				if lineInt, ok := match["line"].(int); ok {
					lineNum = lineInt
					fmt.Printf("DEBUG: Line value (int): %v, type: %T\n", lineInt, lineInt)
				} else if lineFloat, ok := match["line"].(float64); ok {
					lineNum = int(lineFloat)
					fmt.Printf("DEBUG: Line value (float64): %v, type: %T\n", lineFloat, lineFloat)
				} else {
					fmt.Printf("DEBUG: Line not found or wrong type: %T\n", match["line"])
					continue
				}
				
				textContent += fmt.Sprintf("line:%d\n", lineNum)
				fmt.Printf("DEBUG: Added line: %d\n", lineNum)
				if snippet, ok := match["snippet"].(string); ok && snippet != "" {
					// 确保snippet内容正确显示
					textContent += fmt.Sprintf("snippet: %s\n", strings.TrimSpace(snippet))
					fmt.Printf("DEBUG: Added snippet: %s\n", strings.TrimSpace(snippet))
				} else {
					// 如果没有snippet，添加一个默认提示
					textContent += "snippet: [No preview available]\n"
					fmt.Printf("DEBUG: Added default snippet\n")
				}
			}
		} else {
			fmt.Printf("DEBUG: No matches found or wrong type for file %s, type: %T\n", result["path"], result["matches"])
		}
		
		fmt.Printf("DEBUG: Final textContent: %s\n", textContent)
		
		// Create MCP standard content object
		contentObj := map[string]any{
			"type": "text",
			"text": textContent,
			"metadata": map[string]any{
				"path":        result["path"],
				"match_count": result["match_count"],
				"matches":     result["matches"],
			},
		}
		processedResults = append(processedResults, contentObj)
	}

	return map[string]any{
		"content":     processedResults,
		"took_files":  filesScanned,
	}, nil
}

// sortResultsByMatchCount 按匹配数目从多到少排序
func sortResultsByMatchCount(results []map[string]any) {
	// 使用冒泡排序按匹配数目降序排列
	for i := 0; i < len(results)-1; i++ {
		for j := i + 1; j < len(results); j++ {
			countI := results[i]["match_count"].(int)
			countJ := results[j]["match_count"].(int)
			if countI < countJ {
				// 交换位置
				results[i], results[j] = results[j], results[i]
			}
		}
	}
}

// Stream call handler (emit item per file)
func StreamRegexSearch(ctx context.Context, args map[string]any, enc *json.Encoder, flusher http.Flusher) error {
	pattern, _ := args["pattern"].(string)
	if pattern == "" { return fmt.Errorf("pattern required") }
	ci, _ := args["case_insensitive"].(bool)
	limitFiles := 100
	if v, ok := args["limit_files"].(float64); ok { limitFiles = int(v) }
	maxPerFile := 5
	if v, ok := args["max_matches_per_file"].(float64); ok { maxPerFile = int(v) }
	includeSnippet := true
	if v, ok := args["include_snippet"].(bool); ok { includeSnippet = v }
	ctxLines := 1
	if v, ok := args["context_lines"].(float64); ok { ctxLines = int(v) }

	userDirs := extractStringArray(args["directories"]) 
	resolvedDirs, err := resolveDirectories(userDirs)
	if err != nil { return err }
	re, err := compileRegex(pattern, ci)
	if err != nil { return err }

	// 收集所有结果
	results := make([]map[string]any, 0, limitFiles)
	filesScanned := 0
	for _, root := range resolvedDirs {
		if len(results) >= limitFiles { break }
		walkErr := filepath.WalkDir(root, func(path string, d os.DirEntry, err error) error {
			if err != nil { return nil }
			if d.IsDir() { return nil }
			if len(results) >= limitFiles { return io.EOF }
			if isLikelyBinary(path) { return nil }
			matches, err := searchFileForRegex(path, re, maxPerFile, includeSnippet, ctxLines)
			filesScanned++
			if err == nil && len(matches) > 0 {
				var indexDir string
				if cfg, e := config.LoadConfig(); e == nil && len(cfg.FileIndex.IndexDirectories) > 0 { indexDir = cfg.FileIndex.IndexDirectories[0] }
				relPath := path
				if indexDir != "" { relPath = getRelativePath(path, indexDir) }
				
				res := map[string]any{
					"path":        relPath,
					"matches":     matches,
					"match_count": len(matches),
				}
				results = append(results, res)
			}
			return nil
		})
		if walkErr == io.EOF { break }
	}
	
	// 按匹配数目从多到少排序
	sortResultsByMatchCount(results)
	
	// 流式输出排序后的结果
	for _, result := range results {
		// Create text content from file matches
		textContent := fmt.Sprintf("File: %s\nMatch count: %d\n", result["path"], result["match_count"])
		
		// Add match details (最多显示3个匹配)
		if matches, ok := result["matches"].([]map[string]any); ok {
			displayCount := len(matches)
			if displayCount > 3 {
				displayCount = 3
			}
			
			for i := 0; i < displayCount; i++ {
				match := matches[i]
				// 支持int和float64两种类型的行号
				var lineNum int
				if lineInt, ok := match["line"].(int); ok {
					lineNum = lineInt
				} else if lineFloat, ok := match["line"].(float64); ok {
					lineNum = int(lineFloat)
				} else {
					continue
				}
				
				textContent += fmt.Sprintf("line:%d\n", lineNum)
				if snippet, ok := match["snippet"].(string); ok && snippet != "" {
					// 确保snippet内容正确显示
					textContent += fmt.Sprintf("snippet: %s\n", strings.TrimSpace(snippet))
				} else {
					// 如果没有snippet，添加一个默认提示
					textContent += "snippet: [No preview available]\n"
				}
			}
		}
		
		// Create MCP standard content object
		contentObj := map[string]any{
			"type": "text",
			"text": textContent,
			"metadata": map[string]any{
				"path":        result["path"],
				"match_count": result["match_count"],
				"matches":     result["matches"],
			},
		}
		
		_ = enc.Encode(map[string]any{"event": "item", "data": contentObj})
		flusher.Flush()
	}
	
	_ = enc.Encode(map[string]any{"event": "item", "data": map[string]any{"summary": map[string]any{"files": len(results), "took_files": filesScanned}}})
	flusher.Flush()
	return nil
}

func compileRegex(pattern string, caseInsensitive bool) (*regexp.Regexp, error) {
	if caseInsensitive {
		pattern = "(?i)" + pattern
	}
	return regexp.Compile(pattern)
}

// searchFileForRegex scans file line by line to find regex matches, returning either
// only line numbers or also snippets with context.
func searchFileForRegex(path string, re *regexp.Regexp, maxPerFile int, includeSnippet bool, ctxLines int) ([]map[string]any, error) {
	f, err := os.Open(path)
	if err != nil { return nil, err }
	defer f.Close()

	scanner := bufio.NewScanner(f)
	lines := make([]string, 0, 2048)
	for scanner.Scan() { lines = append(lines, scanner.Text()) }
	if err := scanner.Err(); err != nil { return nil, err }

	matches := make([]map[string]any, 0, maxPerFile)
	for i, line := range lines {
		if re.MatchString(line) {
			m := map[string]any{"line": i+1}
			if includeSnippet {
				start := i - ctxLines
				if start < 0 { start = 0 }
				end := i + ctxLines
				if end >= len(lines) { end = len(lines)-1 }
				snippet := strings.Join(lines[start:end+1], "\n")
				// 确保snippet不为空
				if snippet == "" {
					snippet = line // 如果snippet为空，至少包含匹配行
				}
				m["snippet"] = snippet
			}
			matches = append(matches, m)
			if len(matches) >= maxPerFile { break }
		}
	}
	return matches, nil
}

func isLikelyBinary(path string) bool {
	f, err := os.Open(path)
	if err != nil { return true }
	defer f.Close()
	buf := make([]byte, 512)
	n, _ := f.Read(buf)
	for i := 0; i < n; i++ {
		if buf[i] == 0 { return true }
	}
	return false
}
